import {distance} from "./mod.js";
import {sortListByCN} from "./sortName.js";

String.prototype.rstrip = function (chars) {
    let regex = new RegExp(chars + "$");
    return this.replace(regex, "")
};
var showMode = "single";
var searchDriver = "";
var limit_search_show = 200;
var search_type = "video";
var detail_order = "name";
var playRaw = 1;
const request_timeout = 5e3;
const VERSION = "alist v2/v3 20221223";
const UA = "Mozilla/5.0";
const blackNames = ['已刮削', 'PikPak']

function print(any) {
    any = any || "";
    if (typeof any == "object" && Object.keys(any).length > 0) {
        try {
            any = JSON.stringify(any);
            console.log(any)
        } catch (e) {
            console.log(typeof any + ":" + any.length)
        }
    } else if (typeof any == "object" && Object.keys(any).length < 1) {
        console.log("null object")
    } else {
        console.log(any)
    }
}

function getHome(url) {
    if (!url) {
        return ""
    }
    let tmp = url.split("//");
    url = tmp[0] + "//" + tmp[1].split("/")[0];
    try {
        url = decodeURIComponent(url)
    } catch (e) {
    }
    return url
}

const http = function (url, options = {}) {
    if (options.method === "POST" && options.data) {
        options.body = JSON.stringify(options.data);
        options.headers = Object.assign({"content-type": "application/json"}, options.headers)
    }
    options.timeout = request_timeout;
    if (!options.headers) {
        options.headers = {}
    }
    let keys = Object.keys(options.headers).map(it => it.toLowerCase());
    if (!keys.includes("referer")) {
        options.headers["Referer"] = getHome(url)
    }
    if (!keys.includes("user-agent")) {
        options.headers["User-Agent"] = UA
    }
    try {
        const res = req(url, options);
        res.json = () => res && res.content ? JSON.parse(res.content) : null;
        res.text = () => res && res.content ? res.content : "";
        return res
    } catch (e) {
        return {
            json() {
                return null
            }, text() {
                return ""
            }
        }
    }
};
["get", "post"].forEach(method => {
    http[method] = function (url, options = {}) {
        return http(url, Object.assign(options, {method: method.toUpperCase()}))
    }
});
const __drives = {};

function isMedia(file) {
    return /\.(dff|dsf|mp3|aac|wav|wma|cda|flac|m4a|mid|mka|mp2|mpa|mpc|ape|ofr|ogg|ra|wv|tta|ac3|dts|tak|webm|wmv|mpeg|mov|ram|swf|mp4|avi|rm|rmvb|flv|mpg|mkv|m3u8|ts|3gp|asf)$/.test(file.toLowerCase())
}

function get_drives_path(tid) {
    const index = tid.indexOf("$");
    const name = tid.substring(0, index);
    const path = tid.substring(index + 1);
    return {drives: get_drives(name), path: path}
}

function get_drives(name) {
    const {settings, api, server} = __drives[name];
    if (settings.v3 == null) {
        settings.v3 = false;
        const data = http.get(server + "/api/public/settings").json().data;
        if (Array.isArray(data)) {
            settings.title = data.find(x => x.key === "title")?.value;
            settings.v3 = false;
            settings.version = data.find(x => x.key === "version")?.value;
            settings.enableSearch = data.find(x => x.key === "enable search")?.value === "true"
        } else {
            settings.title = data.title;
            settings.v3 = true;
            settings.version = data.version;
            settings.enableSearch = false
        }
        api.path = settings.v3 ? "/api/fs/list" : "/api/public/path";
        api.file = settings.v3 ? "/api/fs/get" : "/api/public/path";
        api.search = settings.v3 ? "/api/public/search" : "/api/public/search"
    }
    return __drives[name]
}

function init(ext) {
    console.log("当前版本号:" + VERSION);
    let data;
    if (typeof ext == "object") {
        data = ext;
        print("alist ext:object")
    } else if (typeof ext == "string") {
        if (ext.startsWith("http")) {
            let alist_data = ext.split(";");
            let alist_data_url = alist_data[0];
            limit_search_show = alist_data.length > 1 ? Number(alist_data[1]) || limit_search_show : limit_search_show;
            search_type = alist_data.length > 2 ? alist_data[2] : search_type;
            print(alist_data_url);
            data = http.get(alist_data_url).json()
        } else {
            print("alist ext:json string");
            data = JSON.parse(ext)
        }
    }
    let drives = [];
    if (Array.isArray(data) && data.length > 0 && data[0].hasOwnProperty("server") && data[0].hasOwnProperty("name")) {
        drives = data
    } else if (!Array.isArray(data) && data.hasOwnProperty("drives") && Array.isArray(data.drives)) {
        drives = data.drives.filter(it => it.type && it.type === "alist" || !it.type)
    }
    print(drives);
    searchDriver = (drives.find(x => x.search) || {}).name || "";
    if (!searchDriver && drives.length > 0) {
        searchDriver = drives[0].name
    }
    print(searchDriver);
    drives.forEach(item => {
        let _path_param = [];
        if (item.params) {
            _path_param = Object.keys(item.params);
            _path_param.sort((a, b) => a.length - b.length)
        }
        if (item.password) {
            let pwdObj = {password: item.password};
            if (!item.params) {
                item.params = {"/": pwdObj}
            } else {
                item.params["/"] = pwdObj
            }
            _path_param.unshift("/")
        }
        __drives[item.name] = {
            name: item.name,
            server: item.server.endsWith("/") ? item.server.rstrip("/") : item.server,
            startPage: item.startPage || "/",
            showAll: item.showAll === true,
            search: !!item.search,
            params: item.params || {},
            _path_param: _path_param,
            settings: {},
            api: {},
            getParams(path) {
                const key = this._path_param.find(x => path.startsWith(x));
                return Object.assign({}, this.params[key], {path: path})
            },
            getPath(path) {
                const res = http.post(this.server + this.api.path, {data: this.getParams(path)}).json();
                return this.settings.v3 ? res.data.content : res.data.files
            },
            getFile(path) {
                let raw_url = this.server + "/d" + path;
                raw_url = encodeURI(raw_url);
                let data = {raw_url: raw_url, raw_url1: raw_url};
                if (playRaw === 1) {
                    try {
                        const res = http.post(this.server + this.api.file, {data: this.getParams(path)}).json();
                        data = this.settings.v3 ? res.data : res.data.files[0];
                        if (!this.settings.v3) {
                            data.raw_url = data.url
                        }
                        data.raw_url1 = raw_url;
                        return data
                    } catch (e) {
                        return data
                    }
                } else {
                    return data
                }
            },
            isFolder(data) {
                return data.type === 1
            },
            isVideo(data) {
                return this.settings.v3 ? data.type === 2 || data.type === 0 || data.type === 3 : data.type === 3 || data.type === 0 || data.type === 4
            },
            is_subt(data) {
                if (data.type === 1) {
                    return false
                }
                const ext = /\.(srt|ass|scc|stl|ttml)$/;
                return ext.test(data.name)
            },
            getPic(data) {
                let pic = this.settings.v3 ? data.thumb : data.thumbnail;
                return pic || (this.isFolder(data) ? "http://img1.3png.com/281e284a670865a71d91515866552b5f172b.png" : "")
            },
            getTime(data, isStandard) {
                isStandard = isStandard || false;
                try {
                    let tTime = data.updated_at || data.time_str || data.modified || "";
                    let date = "";
                    if (tTime) {
                        tTime = tTime.split("T");
                        date = tTime[0];
                        if (isStandard) {
                            date = date.replace(/-/g, "/")
                        }
                        tTime = tTime[1].split(/Z|\./);
                        date += " " + tTime[0]
                    }
                    return date
                } catch (e) {
                    return ""
                }
            }
        }
    });
    print("init执行完毕")
}

function home(filter) {
    let classes = Object.keys(__drives).map(key => ({
        type_id: `${key}$${__drives[key].startPage}`,
        type_name: key,
        type_flag: "1"
    }));
    let filter_dict = {};
    let filters = [{
        key: "order",
        name: "排序",
        value: [{n: "名称⬆️", v: "vod_name_asc"}, {n: "名称⬇️", v: "vod_name_desc"}, {
            n: "中英⬆️",
            v: "vod_cn_asc"
        }, {n: "中英⬇️", v: "vod_cn_desc"}, {n: "时间⬆️", v: "vod_time_asc"}, {n: "时间⬇️", v: "vod_time_desc"}, {
            n: "大小⬆️",
            v: "vod_size_asc"
        }, {n: "大小⬇️", v: "vod_size_desc"}, {n: "无", v: "none"}]
    }, {key: "show", name: "播放展示", value: [{n: "单集", v: "single"}, {n: "全集", v: "all"}]}];
    classes.forEach(it => {
        filter_dict[it.type_id] = filters
    });
    print("----home----");
    print(classes);
    return JSON.stringify({class: classes, filters: filter_dict})
}

function homeVod(params) {
    let _post_data = {pageNum: 0, pageSize: 100};
    let _post_url = "https://pbaccess.video.qq.com/trpc.videosearch.hot_rank.HotRankServantHttp/HotRankHttp";
    let data = http.post(_post_url, {data: _post_data}).json();
    let _list = [];
    try {
        data = data["data"]["navItemList"][0]["hotRankResult"]["rankItemList"];
        data.forEach(it => {
            _list.push({
                vod_name: it.title,
                vod_id: "msearch:" + it.title,
                vod_pic: "https://avatars.githubusercontent.com/u/97389433?s=120&v=4",
                vod_remarks: it.changeOrder
            })
        })
    } catch (e) {
        print("Alist获取首页推荐发送错误:" + e.message)
    }
    return JSON.stringify({list: _list})
}

function category(tid, pg, filter, extend) {
    let orid = tid.replace(/#all#|#search#/g, "");
    let {drives, path} = get_drives_path(orid);
    const id = orid.endsWith("/") ? orid : orid + "/";
    const list = drives.getPath(path);
    let subList = [];
    let vodFiles = [];
    let allList = [];
    let fl = filter ? extend : {};
    if (fl.show) {
        showMode = fl.show
    }
    list.forEach(item => {
        if (drives.is_subt(item)) {
            subList.push(item.name)
        }
        if (!drives.showAll && !drives.isFolder(item) && !drives.isVideo(item)) {
            return
        }
        let vod_time = drives.getTime(item);
        let vod_size = get_size(item.size);
        let remark = vod_time.split(" ")[0].substr(3) + "\t" + vod_size;
        let vod_id = id + item.name + (drives.isFolder(item) ? "/" : "");
        if (showMode === "all") {
            vod_id += "#all#"
        }
        print(vod_id);
        const vod = {
            vod_id: vod_id,
            vod_name: item.name.replaceAll("$", "").replaceAll("#", ""),
            vod_pic: drives.getPic(item),
            vod_time: vod_time,
            vod_size: item.size,
            vod_tag: drives.isFolder(item) ? "folder" : "file",
            vod_remarks: drives.isFolder(item) ? remark + " 文件夹" : remark
        };
        if (drives.isVideo(item)) {
            vodFiles.push(vod)
        }
        allList.push(vod)
    });
    if (vodFiles.length === 1 && subList.length > 0) {
        let sub;
        if (subList.length === 1) {
            sub = subList[0]
        } else {
            let subs = JSON.parse(JSON.stringify(subList));
            subs.sort((a, b) => {
                let a_similar = (a.includes("chs") ? 100 : 0) + levenshteinDistance(a, vodFiles[0].vod_name);
                let b_similar = (b.includes("chs") ? 100 : 0) + levenshteinDistance(b, vodFiles[0].vod_name);
                if (a_similar > b_similar) {
                    return 1
                } else {
                    return -1
                }
            });
            sub = subs.slice(-1)[0]
        }
        vodFiles[0].vod_id += "@@@" + sub;
        vodFiles[0].vod_remarks += "🏷️"
    } else {
        vodFiles.forEach(item => {
            const lh = 0;
            let sub;
            subList.forEach(s => {
                const l = levenshteinDistance(s, item.vod_name);
                if (l > 60 && l > lh) {
                    sub = s
                }
            });
            if (sub) {
                item.vod_id += "@@@" + sub;
                item.vod_remarks += "🏷️"
            }
        })
    }
    if (fl.order) {
        let key = fl.order.split("_").slice(0, -1).join("_");
        let order = fl.order.split("_").slice(-1)[0];
        print(`排序key:${key},排序order:${order}`);
        if (key.includes("name")) {
            detail_order = "name";
            allList = sortListByName(allList, key, order)
        } else if (key.includes("cn")) {
            detail_order = "cn";
            allList = sortListByCN(allList, "vod_name", order)
        } else if (key.includes("time")) {
            detail_order = "time";
            allList = sortListByTime(allList, key, order)
        } else if (key.includes("size")) {
            detail_order = "size";
            allList = sortListBySize(allList, key, order)
        } else if (fl.order.includes("none")) {
            detail_order = "none";
            print("不排序")
        }
    } else {
        if (detail_order !== "none") {
            allList = sortListByName(allList, "vod_name", "asc")
        }
    }
    print("----category----" + `tid:${tid},detail_order:${detail_order},showMode:${showMode}`);
    return JSON.stringify({page: 1, pagecount: 1, limit: allList.length, total: allList.length, list: allList})
}

function getAll(otid, tid, drives, path) {
    try {
        const content = category(tid, null, false, null);
        const isFile = isMedia(otid.replace(/#all#|#search#/g, "").split("@@@")[0]);
        const {list} = JSON.parse(content);
        let vod_play_url = [];
        list.forEach(x => {
            if (x.vod_tag === "file") {
                let vid = x.vod_id.replace(/#all#|#search#/g, "");
                vod_play_url.push(`${x.vod_name}$${vid.substring(vid.indexOf("$") + 1)}`)
            }
        });
        const pl = path.split("/").filter(it => it);
        let vod_name = pl[pl.length - 1] || drives.name;
        if (vod_name === drives.name) {
            print(pl)
        }
        if (otid.includes("#search#")) {
            vod_name += "[搜]"
        }
        let vod = {
            vod_id: otid,
            vod_name: vod_name,
            type_name: "文件夹",
            vod_pic: "https://avatars.githubusercontent.com/u/97389433?s=120&v=4",
            vod_content: tid,
            vod_tag: "folder",
            vod_play_from: drives.name,
            vod_play_url: vod_play_url.join("#"),
            vod_remarks: drives.settings.title
        };
        print("----detail1----");
        print(vod);
        return JSON.stringify({list: [vod]})
    } catch (e) {
        print(e.message);
        let list = [{
            vod_name: "无数据,防无限请求",
            type_name: "文件夹",
            vod_id: "no_data",
            vod_remarks: "不要点,会崩的",
            vod_pic: "https://ghproxy.com/https://raw.githubusercontent.com/hjdhnx/dr_py/main/404.jpg",
            vod_actor: e.message,
            vod_director: tid,
            vod_content: otid
        }];
        return JSON.stringify({list: list})
    }
}

function detail(tid) {
    let isSearch = tid.includes("#search#");
    let isAll = tid.includes("#all#");
    let otid = tid;
    tid = tid.replace(/#all#|#search#/g, "");
    let isFile = isMedia(tid.split("@@@")[0]);
    print(`isFile:${tid}?${isFile}`);
    let {drives, path} = get_drives_path(tid);
    print(`drives:${drives},path:${path},`);
    if (path.endsWith("/")) {
        return getAll(otid, tid, drives, path)
    } else {
        if (isSearch && !isFile) {
            return getAll(otid, tid, drives, path)
        } else if (isAll) {
            let new_tid;
            if (isFile) {
                new_tid = tid.split("/").slice(0, -1).join("/") + "/"
            } else {
                new_tid = tid
            }
            print(`全集模式 tid:${tid}=>tid:${new_tid}`);
            let {drives, path} = get_drives_path(new_tid);
            return getAll(otid, new_tid, drives, path)
        } else if (isFile) {
            let paths = path.split("@@@");
            let vod_name = paths[0].substring(paths[0].lastIndexOf("/") + 1);
            let vod_title = vod_name;
            if (otid.includes("#search#")) {
                vod_title += "[搜]"
            }
            let vod = {
                vod_id: otid,
                vod_name: vod_title,
                type_name: "文件",
                vod_pic: "https://avatars.githubusercontent.com/u/97389433?s=120&v=4",
                vod_content: tid,
                vod_play_from: drives.name,
                vod_play_url: vod_name + "$" + path,
                vod_remarks: drives.settings.title
            };
            print("----detail2----");
            print(vod);
            return JSON.stringify({list: [vod]})
        } else {
            return JSON.stringify({list: []})
        }
    }
}

function play(flag, id, flags) {
    const drives = get_drives(flag);
    const urls = id.split("@@@");
    let vod = {parse: 0, playUrl: "", url: drives.getFile(urls[0]).raw_url};
    if (urls.length >= 2) {
        const path = urls[0].substring(0, urls[0].lastIndexOf("/") + 1);
        vod.subt = drives.getFile(path + urls[1]).raw_url1
    }
    print("----play----");
    print(vod);
    return JSON.stringify(vod)
}

function search(wd, quick) {
    print(__drives);
    print("可搜索的alist驱动:" + searchDriver);
    if (!searchDriver || !wd) {
        return JSON.stringify({list: []})
    } else {
        let driver = __drives[searchDriver];
        wd = wd.split(" ").filter(it => it.trim()).join("+");
        print(driver);
        let surl = driver.server + "/search?box=" + wd + "&url=";
        if (search_type) {
            surl += "&type=" + search_type
        }
        print("搜索链接:" + surl);
        let html = http.get(surl).text();
        let lists = [];
        try {
            lists = pdfa(html, "div&&ul&&a")
        } catch (e) {
        }
        lists = lists.filter(it => {
            let vhref = pdfh(it, "a&&href");
            return !blackNames.find(blackName => vhref.indexOf(blackName) !== -1)
        });

        print(`搜索结果数:${lists.length},搜索结果显示数量限制:${limit_search_show}`);
        let vods = [];
        let excludeReg = /\.(pdf|epub|mobi|txt|doc|lrc)$/;
        let cnt = 0;
        lists.forEach(it => {
            let vhref = pdfh(it, "a&&href");
            if (vhref) {
                vhref = unescape(vhref)
            }
            if (excludeReg.test(vhref)) {
                return
            }
            if (cnt < limit_search_show) {
                print(vhref)
            }
            cnt++;
            let vid = searchDriver + "$" + vhref + "#search#";
            if (showMode === "all") {
                vid += "#all#"
            }
            vods.push({
                vod_name: pdfh(it, "a&&Text"),
                vod_id: vid,
                vod_tag: isMedia(vhref) ? "file" : "folder",
                vod_pic: "http://img1.3png.com/281e284a670865a71d91515866552b5f172b.png",
                vod_remarks: searchDriver
            })
        });
        vods = vods.slice(0, limit_search_show);
        print(vods);
        return JSON.stringify({list: vods})
    }
}

function get_size(sz) {
    if (sz <= 0) {
        return ""
    }
    let filesize = "";
    if (sz > 1024 * 1024 * 1024 * 1024) {
        sz /= 1024 * 1024 * 1024 * 1024;
        filesize = "TB"
    } else if (sz > 1024 * 1024 * 1024) {
        sz /= 1024 * 1024 * 1024;
        filesize = "GB"
    } else if (sz > 1024 * 1024) {
        sz /= 1024 * 1024;
        filesize = "MB"
    } else if (sz > 1024) {
        sz /= 1024;
        filesize = "KB"
    } else {
        filesize = "B"
    }
    let sizeStr = sz.toFixed(2) + filesize, index = sizeStr.indexOf("."), dou = sizeStr.substr(index + 1, 2);
    if (dou === "00") {
        return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2)
    } else {
        return sizeStr
    }
}

function levenshteinDistance(str1, str2) {
    return 100 - 100 * distance(str1, str2) / Math.max(str1.length, str2.length)
}

function naturalSort(options) {
    if (!options) {
        options = {}
    }
    return function (a, b) {
        if (options.key) {
            a = a[options.key];
            b = b[options.key]
        }
        var EQUAL = 0;
        var GREATER = options.order === "desc" ? -1 : 1;
        var SMALLER = -GREATER;
        var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi;
        var sre = /(^[ ]*|[ ]*$)/g;
        var dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/;
        var hre = /^0x[0-9a-f]+$/i;
        var ore = /^0/;
        var normalize = function normalize(value) {
            var string = "" + value;
            return options.caseSensitive ? string : string.toLowerCase()
        };
        var x = normalize(a).replace(sre, "") || "";
        var y = normalize(b).replace(sre, "") || "";
        var xN = x.replace(re, "\0$1\0").replace(/\0$/, "").replace(/^\0/, "").split("\0");
        var yN = y.replace(re, "\0$1\0").replace(/\0$/, "").replace(/^\0/, "").split("\0");
        if (!x && !y) return EQUAL;
        if (!x && y) return GREATER;
        if (x && !y) return SMALLER;
        var xD = parseInt(x.match(hre)) || xN.length != 1 && x.match(dre) && Date.parse(x);
        var yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null;
        var oFxNcL, oFyNcL;
        if (yD) {
            if (xD < yD) return SMALLER; else if (xD > yD) return GREATER
        }
        for (var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
            oFxNcL = !(xN[cLoc] || "").match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
            oFyNcL = !(yN[cLoc] || "").match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
            if (isNaN(oFxNcL) !== isNaN(oFyNcL)) return isNaN(oFxNcL) ? GREATER : SMALLER; else if (typeof oFxNcL !== typeof oFyNcL) {
                oFxNcL += "";
                oFyNcL += ""
            }
            if (oFxNcL < oFyNcL) return SMALLER;
            if (oFxNcL > oFyNcL) return GREATER
        }
        return EQUAL
    }
}

const sortListByName = (vodList, key, order) => {
    if (!key) {
        return vodList
    }
    order = order || "asc";
    return vodList.sort(naturalSort({key: key, order: order, caseSensitive: true}))
};
const getTimeInt = timeStr => {
    return new Date(timeStr).getTime()
};
const sortListByTime = (vodList, key, order) => {
    if (!key) {
        return vodList
    }
    let ASCarr = vodList.sort((a, b) => {
        a = a[key];
        b = b[key];
        return getTimeInt(a) - getTimeInt(b)
    });
    if (order === "desc") {
        ASCarr.reverse()
    }
    return ASCarr
};
const sortListBySize = (vodList, key, order) => {
    if (!key) {
        return vodList
    }
    let ASCarr = vodList.sort((a, b) => {
        a = a[key];
        b = b[key];
        return (Number(a) || 0) - (Number(b) || 0)
    });
    if (order === "desc") {
        ASCarr.reverse()
    }
    return ASCarr
};
export default {
    init: init,
    home: home,
    homeVod: homeVod,
    category: category,
    detail: detail,
    play: play,
    search: search
};